package pl.edu.agh.ssm.monitor.listeners;

import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;

import pl.edu.agh.ssm.monitor.data.Packet;
import pl.edu.agh.ssm.monitor.data.RTSPAnswer;
import pl.edu.agh.ssm.monitor.data.RTSPPacket;
import pl.edu.agh.ssm.monitor.data.RTSPPacketType;
import pl.edu.agh.ssm.monitor.data.Session;
import pl.edu.agh.ssm.monitor.data.SessionConnection;
import pl.edu.agh.ssm.monitor.data.SessionDataContainer;
import pl.edu.agh.ssm.monitor.data.SessionMedia;
import pl.edu.agh.ssm.monitor.data.SessionNode;
import pl.edu.agh.ssm.monitor.data.SessionNodeDescription;
import pl.edu.agh.ssm.monitor.data.SessionNodeType;
import pl.edu.agh.ssm.monitor.net.PacketFilter;
import pl.edu.agh.ssm.monitor.net.PacketReceiver;
import pl.edu.agh.ssm.monitor.parsers.rtsp.RTSPAnswerParser;
import pl.edu.agh.ssm.monitor.utils.SessionDataUtils;

public class RTSPAnswersListener implements PacketListener {

	private int priority;
	private Session rtspSession = null;
	private SessionConnection rtspControlConnection = null;
	private SessionDataContainer<Session> sessions;
	private PacketReceiver receiver;
	private HashMap<Integer, SessionMedia> rtpmap;

	private List<PacketListener> listeners = new LinkedList<PacketListener>();
	private List<PacketFilter> filters = new LinkedList<PacketFilter>();

	public RTSPAnswersListener(SessionDataContainer<Session> sessions,
			Session rtspSession, SessionConnection rtspControlConnection,
			PacketReceiver receiver, HashMap<Integer, SessionMedia> rtpmap) {
		this.rtspSession = rtspSession;
		this.rtspControlConnection = rtspControlConnection;
		this.sessions = sessions;
		this.receiver = receiver;
		this.rtpmap = rtpmap;
	}

	@Override
	public int getPriority() {
		return priority;
	}

	@Override
	public void processPacket(Packet packet) {
		RTSPAnswer answer = new RTSPAnswerParser().parse(packet.getData());
		if (answer != null) {
//			System.out.println("--------------------------------");
//			System.out.println("ANSWER to packet: " + answer.getCSeq());
//			System.out.println("--------------------------------");
			if (answer.getSdp() != null) {
				// System.out.println("ANSWER: SDP found");
				// System.out.println(answer.getSdp().getName());
				// System.out.println(answer.getSdp().getOwner());
				SessionDataUtils.copySessionDescription(answer.getSdp(),
						rtspSession.getSessionDescription());
				// rtspSession.setSessionDescription(answer.getSdp());
				sessions.update(rtspSession);
			}

			if (answer.getCSeq() != 0) {
				for (RTSPPacket rtsp : rtspControlConnection.getRtspPackets()) {
					if (rtsp.getCSeq() == answer.getCSeq()) {
						rtsp.setAnswer(answer);
						if (rtsp.getType() == RTSPPacketType.SETUP) {
							createListeners(answer, packet);
						}
						break;
					}
				}
			}
		}

	}

	@Override
	public void setPriority(int priority) {
		this.priority = priority;
	}

	public void removeAllListeners() {
		for (PacketListener listener : listeners) {
			receiver.removePacketListener(listener);
		}
	}

	private void createListeners(RTSPAnswer answer, Packet packet) {
//		System.out.println("FULL ANSWER: " + answer.getFullPacket());
		if (answer.getAnswerCode() == 200) { // OK
			// try to find
			String[] tmp = answer.getFullPacket().split("\n");
			for (int i = 0; i < tmp.length; i++) {
				// System.out.println("Check line: " + tmp[i]);
				if (tmp[i].startsWith("Transport:")) {
					// System.out.println("Transport line");
					String[] tmp2 = tmp[i].split(";");
					String[] client_ports = null;
					String[] server_ports = null;
					String ssrc = null;
					for (int j = 0; j < tmp2.length; j++) {
						if (tmp2[j].startsWith("client_port=")) {
							client_ports = tmp2[j].substring(
									tmp2[j].lastIndexOf('=') + 1).split("-");
						} else if (tmp2[j].startsWith("server_port=")) {
							server_ports = tmp2[j].substring(
									tmp2[j].lastIndexOf('=') + 1).split("-");
						} else if (tmp2[j].startsWith("ssrc=")) {
							ssrc = tmp2[j]
									.substring(tmp2[j].lastIndexOf('=') + 1, tmp2[j].lastIndexOf('=') + 9);
							System.out.println("SSRC:" + ssrc + ", length:" + ssrc.length());
						}
					}

					try {
						System.out.println("Client: " + client_ports[0]
								+ ", server: " + server_ports[0]);

						System.out.println("ADD server -> client data");
						createListener(
								packet.getDestAddress().getHostAddress(),
								Integer.valueOf(client_ports[0]), packet
										.getSourceAddress().getHostAddress(),
								Integer.valueOf(server_ports[0])); // data from
																	// server to
																	// client
						System.out.println("ADD server -> client control");
						createListener(
								packet.getDestAddress().getHostAddress(),
								Integer.valueOf(client_ports[1]), packet
										.getSourceAddress().getHostAddress(),
								Integer.valueOf(server_ports[1])); // rtcp from
																	// server to
																	// client
						System.out.println("ADD client -> server control");
						createListener(packet.getSourceAddress()
								.getHostAddress(), Integer
								.valueOf(server_ports[1]), packet
								.getDestAddress().getHostAddress(), Integer
								.valueOf(client_ports[1])); // data from server
															// to client

						// TODO: add new node to session
						SessionNode node = new SessionNode(packet
								.getSourceAddress(), rtspSession.getId());
						
						node.setSsrc(Long.parseLong(ssrc.toLowerCase(), 16));
						node.setNodeType(SessionNodeType.SENDER);
						node.setNodeDescription(new SessionNodeDescription());
						rtspSession.addNode(node);
					} catch (Exception e) {
						e.printStackTrace();
					}
					break;
				}
			}
		}
	}

	private void createListener(String address, int port, String sourceAddress,
			int sourcePort) {

		boolean addListener = true;
		for (PacketFilter filter : filters) {
			try {
				if (filter.matches(port, InetAddress.getByName(address), InetAddress.getByName(sourceAddress), sourcePort)) {
					addListener = false;
					break;
				}
			} catch (UnknownHostException e) {
				
			}
		}

		if (addListener) {
			if (port % 2 == 0) { // rtp listener

				RTPDataListener listener = new RTPDataListener(rtspSession);
				listener.setPriority(priority - 1);
				RtpAVPMediaTypeInfoProvider mediaTypeInfoProvider = new RtpAVPMediaTypeInfoProvider();
				if (rtpmap != null) {
					for (Integer key : rtpmap.keySet()) {
						mediaTypeInfoProvider.addMediaTypeInfo(key, rtpmap
								.get(key));
					}
				}
				listener.setMediaTypeInfoProvider(mediaTypeInfoProvider);
				PacketFilter rtpFilter = new PacketFilter();
				rtpFilter.setAddress(address);
				rtpFilter.setPort(port);
				rtpFilter.setSourceAddress(sourceAddress);
				rtpFilter.setSourcePort(sourcePort);

				filters.add(rtpFilter);
				listeners.add(listener);
				receiver.addPacketListenerToSession(listener, rtpFilter, rtspSession.getId());

			} else {
				RTCPDataListener listener = new RTCPDataListener(rtspSession);
				listener.setPriority(priority - 1);
				PacketFilter rtcpFilter = new PacketFilter();
				rtcpFilter.setAddress(address);
				rtcpFilter.setPort(port);
				rtcpFilter.setSourceAddress(sourceAddress);
				rtcpFilter.setSourcePort(sourcePort);

				filters.add(rtcpFilter);
				listeners.add(listener);
				receiver.addPacketListenerToSession(listener, rtcpFilter, rtspSession.getId());
			}
		}

	}
}
